{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# python函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 函数定义\n",
    "$\\sum_{n=1}^{100}n$是1+2+3+...+100的抽象，同样函数也是运算过程的抽象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用取绝对值函数: 7\n"
     ]
    }
   ],
   "source": [
    "# 函数调用\n",
    "out = abs(-7)\n",
    "print('1.调用取绝对值函数:', out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用自定义的绝对值函数: 100\n"
     ]
    }
   ],
   "source": [
    "from test_model import my_abs       # 从自己编写的模块中调用函数\n",
    "out = my_abs(-100)\n",
    "print('1.调用自定义的绝对值函数:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 函数的参数\n",
    "1. 位置参数\n",
    "2. 默认参数：    **定义默认参数要牢记一点，默认参数必须指向不变对象**\n",
    "3. 可变参数：    *args是可变参数，args接收的是一个tuple\n",
    "4. 关键字参数：  **kw是关键字参数，kw接收的是一个dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.函数返回多个值: x=101.73205080756888, y=101.0\n",
      "2.函数返回多个值本质是一个元组: (101.73205080756888, 101.0) <class 'tuple'>\n"
     ]
    }
   ],
   "source": [
    "# 1,2位置和默认参数\n",
    "import math\n",
    "def move(x, y, step, angle=0):              # x,y,step是位置参数，angle=0是默认参数，其位置要靠后\n",
    "    nx = x + step*math.cos(angle)\n",
    "    ny = y + step*math.sin(angle)\n",
    "    return nx, ny            \n",
    "x, y = move(100, 100, 2, math.pi / 6)\n",
    "print('1.函数返回多个值: x={0}, y={1}'.format(x, y))\n",
    "out = move(100, 100, 2, math.pi / 6)       \n",
    "print('2.函数返回多个值本质是一个元组:', out, type(out))       # 函数返回多个值,其实是一个tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.可变参数输入: 14\n",
      "2.可变参数的列表输入: 30\n"
     ]
    }
   ],
   "source": [
    "# 3.可变参数，可传人0个或任意个参数, 参数组织为元组形式\n",
    "def calc(*numbers):  # 不用将参数用列表或元组的形式传入\n",
    "    sum = 0\n",
    "    for i in numbers:\n",
    "        sum += i*i\n",
    "    return sum\n",
    "out = calc(1,2,3)\n",
    "print('1.可变参数输入:', out)\n",
    "out = calc(*[1,2,3,4])\n",
    "print('2.可变参数的列表输入:', out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: David age: 25 others: {'gender': 'man', 'job': 'Engineer'}\n",
      "name: Jack age: 25 others: {'gender': 'man', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "# 4.关键字参数，组织为字典形式，可传入多个参数\n",
    "def person(name, age, **kw):\n",
    "    print('name:', name, 'age:', age, 'others:', kw)\n",
    "person('David', 25, gender='man', job='Engineer')\n",
    "person('Jack', 25, **{'gender': 'man', 'job':'Engineer'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 Beijing Engineer\n",
      "Jack 24 ('man', 123) Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "# 5.命名关键字参数\n",
    "def person(name, age, *, city, job):   # 使用 * 隔开位置参数和命名关键字参数\n",
    "    print(name, age, city, job)\n",
    "person('Jack', 24, city='Beijing', job='Engineer')\n",
    "def person2(name, age, *args, city,job):     # args为可变参数 \n",
    "    print(name, age, args, city, job)\n",
    "person2('Jack', 24, 'man', 123, city='Beijing', job='Engineer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.参数组合结果: a = 1 b = 2 c = 3 args = ('a', 'b') name= 小明 kw = {'x': 99}\n",
      "2.参数组合结果: a = 1 b = 2 c = 3 args = (4, 5) name= 李华 kw = {'x': 100, 'score': 100}\n"
     ]
    }
   ],
   "source": [
    "# 6.参数组合:必选参数、默认参数、可变参数、命名关键字参数和关键字参数\n",
    "def f1(a, b, c=0, *args, name='David', **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'name=', name, 'kw =', kw)\n",
    "print('1.参数组合结果:', end=' ')\n",
    "f1(1, 2, 3, 'a', 'b', name='小明', x=99)\n",
    "args = (1,2,3,4,5)                         # 对于任意函数，都可以通过类似func(*args, **kw)的形式调用\n",
    "kw = {'name':'李华', 'x' :100, 'score':100}\n",
    "print('2.参数组合结果:', end=' ')\n",
    "f1(*args, **kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 函数的递归操作\n",
    "函数内部调用自己"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用递归函数得出: 1 120\n"
     ]
    }
   ],
   "source": [
    "def fact(n):      \n",
    "    if n == 1:\n",
    "        return 1\n",
    "    return n * fact(n - 1)\n",
    "out = fact(1)\n",
    "out1 = fact(5)   # 如果递归的数很大，会出现递归堆栈溢出\n",
    "print('1.使用递归函数得出:', out, out1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120\n"
     ]
    }
   ],
   "source": [
    "# 解决递归调用栈溢出的方法是通过尾递归优化\n",
    "# 要求：return语句不能包含表达式\n",
    "def fact_iter(num, product):\n",
    "    if num == 1:\n",
    "        return product\n",
    "    return fact_iter(num-1, num * product)\n",
    "out = fact_iter(5, 1)   # 由于python解释器没有进行尾递归优化，所以仍出现堆栈溢出\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 汉诺塔练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.移动盘子的过程:\n",
      "A ---> C\n",
      "A ---> B\n",
      "C ---> B\n",
      "A ---> C\n",
      "B ---> A\n",
      "B ---> C\n",
      "A ---> C\n"
     ]
    }
   ],
   "source": [
    "def move(n, a,b,c):    # 把n个盘子，借助b(第3个参数)，从a(第2个参数)移到c(第4个参数)\n",
    "    if n == 1:\n",
    "        print(a, '--->', c)\n",
    "    else:\n",
    "        move(n-1, a, c, b)    # 将n-1个盘子移动到b(借助c)\n",
    "        move(1, a, b, c)      # 将a的1个盘子移动到c(借助b)\n",
    "        move(n-1, b, a, c)    # 将b的n-1个盘子移动到c(借助a)\n",
    "print('1.移动盘子的过程:')\n",
    "move(3, 'A','B','C') "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 高级特性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 切片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.原始列表: ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']\n",
      "  列表从开头切片: ['Michael', 'Sarah', 'Tracy']\n",
      "  列表从末尾切片: ['Tracy', 'Bob', 'Jack']\n",
      "2.原始列表: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]\n",
      "  前10个每隔2个取一个: [0, 2, 4, 6, 8]\n",
      "  所有数每隔2个取一个: [0, 2, 4, 6, 8, 10, 12, 14]\n",
      "  列表的复制: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]\n"
     ]
    }
   ],
   "source": [
    "L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']\n",
    "print('1.原始列表:', L)\n",
    "out = L[:3]        # L[0:3], L[m:n] 取n-m个元素，索引不包含n\n",
    "print('  列表从开头切片:', out)\n",
    "out = L[-3:]       # 最后一个元素索引-1\n",
    "print('  列表从末尾切片:', out)\n",
    "L = list(range(15))\n",
    "print('2.原始列表:', L)\n",
    "out = L[:10:2]\n",
    "print('  前10个每隔2个取一个:', out)\n",
    "out = L[::2]\n",
    "print('  所有数每隔2个取一个:', out)\n",
    "out = L[:]\n",
    "print('  列表的复制:', out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.元组的切片仍是元组: (0, 1, 2) <class 'tuple'>\n",
      "2.字符串的切片仍是字符串: aceg <class 'str'>\n"
     ]
    }
   ],
   "source": [
    "out = tuple(range(10))[0:3]\n",
    "print('1.元组的切片仍是元组:', out, type(out))\n",
    "out = 'abcdefg'[::2]\n",
    "print('2.字符串的切片仍是字符串:', out, type(out))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "切片练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.去除空格后的结果: **abced** 长度: 5\n"
     ]
    }
   ],
   "source": [
    "def trim(s):\n",
    "    if s[0] == ' ':\n",
    "        return trim(s[1:])     # 使用递归\n",
    "    elif s[-1] == ' ':\n",
    "        return trim(s[:-2])\n",
    "    return s\n",
    "out = trim('   abcedf   ')\n",
    "print('1.去除空格后的结果:', '**' + out + '**', '长度:', len(out))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 迭代"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.列表迭代: 1 2 3 4 \n",
      "2.元组迭代: 1 2 3 4 5 \n",
      "3.字典迭代:\n",
      "  默认只使用键: a b c 4 \n",
      "  只使用键key: a b c 4 \n",
      "  只使用键value: 1 2 3 d \n",
      "  同时使用key和value: (a, 1) (b, 2) (c, 3) (4, d) "
     ]
    }
   ],
   "source": [
    "# 使用for循环时，只要作用于一个可迭代对象，for循环就可以正常运行\n",
    "# 1.列表迭代\n",
    "print('1.列表迭代:', end =' ')\n",
    "iteration = [1,2,3,4]\n",
    "for i in iteration:\n",
    "    print(i, end = ' ')\n",
    "# 2.元组迭代\n",
    "print('\\n2.元组迭代:', end =' ')\n",
    "iteration = (1,2,3,4,5)\n",
    "for i in iteration:\n",
    "    print(i, end = ' ')\n",
    "# 3.字典的迭代\n",
    "iteration = {'a':1, 'b':2, 'c':3, 4:'d'}\n",
    "print('\\n3.字典迭代:')\n",
    "print('  默认只使用键:', end=' ')\n",
    "for key in iteration:\n",
    "    print(key, end=' ')\n",
    "print('\\n  只使用键key:', end=' ')\n",
    "for key in iteration.keys():\n",
    "    print(key, end = ' ')\n",
    "print('\\n  只使用键value:', end=' ')\n",
    "for value in iteration.values():\n",
    "    print(value, end = ' ')\n",
    "print('\\n  同时使用key和value:', end=' ')\n",
    "for key, value in iteration.items():\n",
    "    print('(%s, %s)' % (key, value), end = ' ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.字符串类型可以迭代: True\n",
      "1.列表类型可以迭代: True\n",
      "1.整型类型不可以迭代: False\n"
     ]
    }
   ],
   "source": [
    "from collections import Iterable\n",
    "print('1.字符串类型可以迭代:', isinstance('abc', Iterable))\n",
    "print('1.列表类型可以迭代:', isinstance([1,2,3], Iterable))\n",
    "print('1.整型类型不可以迭代:', isinstance(123, Iterable))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.单个元素为2元组的列表迭代:\n",
      "x=1, y=a x=2, y=b x=3, y=c \n",
      "(1, 'a') (2, 'b') (3, 'c') \n",
      "2.使用enumerate访问:\n",
      "x=0, y=a x=1, y=b x=2, y=c \n",
      "(0, 'a') (1, 'b') (2, 'c') "
     ]
    }
   ],
   "source": [
    "# 迭代单个元素为2元组的列表\n",
    "print('1.单个元素为2元组的列表迭代:')\n",
    "for x, y in [(1,'a'), (2, 'b'), (3, 'c')]:\n",
    "    print('x={0}, y={1}'.format(x, y), end= ' ')\n",
    "print()\n",
    "for z in [(1,'a'), (2, 'b'), (3, 'c')]:    # 单个元素为2元组，可用 x,y 的形式分别获取\n",
    "    print('{0}'.format(z), end=' ')\n",
    "    \n",
    "print('\\n2.使用enumerate访问:')\n",
    "for x, y in enumerate(['a', 'b', 'c']):\n",
    "    print('x={0}, y={1}'.format(x, y), end= ' ')\n",
    "print()\n",
    "for z in enumerate(['a', 'b', 'c']):\n",
    "    print(z, end=' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 列表生成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.列表生成的结果: [1, 4, 9, 16, 25, 36]\n",
      "2.加入判断生成的结果: [4, 16, 36]\n",
      "3.双层循环生成全排列: [('a', '1'), ('a', '2'), ('b', '1'), ('b', '2')]\n"
     ]
    }
   ],
   "source": [
    "# List Comprehensions\n",
    "out = [x *x for x in range(1,7)]\n",
    "print('1.列表生成的结果:', out)\n",
    "out = [x * x for x in range(1,7) if x % 2 == 0]   # 可加入判断\n",
    "print('2.加入判断生成的结果:', out)\n",
    "out = [(x, y) for x in 'ab' for y in '12']       # 相当于双重循环，循环次数为2*2\n",
    "print('3.双层循环生成全排列:', out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.字典生成的结果: {1: 'a', 2: 'b', 3: 'c'}\n"
     ]
    }
   ],
   "source": [
    "# 字典也有生成\n",
    "dictionary = {'a':1, 'b':2, 'c':3}\n",
    "out =  {v:k for k, v in dictionary.items()}   # 字典生成主要 key:value 分开\n",
    "print('1.字典生成的结果:',out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "列表生成练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['hello', 'world', 'apple']\n"
     ]
    }
   ],
   "source": [
    "L = ['Hello', 'World', 18, 'Apple', None]\n",
    "out = [s.lower() for s in L if isinstance(s, str)]\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 生成器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用()创建生成器: <class 'generator'>\n",
      "2.使用for循环方法数据:1 4 9 16 25 36 "
     ]
    }
   ],
   "source": [
    "# 1.使用()生成创建生成器\n",
    "out = (x * x for x in range(1,7))\n",
    "print('1.使用()创建生成器:', type(out))\n",
    "# print('2.next()方法调用用一个值:', next(out))  # 所有数据迭代后报错：StopIteration\n",
    "print('2.使用for循环方法数据:', end='')\n",
    "for x in out:\n",
    "    print(x, end= ' ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.生成器返回的是一个对象: <generator object fib at 0x000002C2E6A01DB0>\n",
      "2.使用for循环访问yield的输出: 1 1 2 3 5 8 g: 1\n",
      "g: 1\n",
      "g: 2\n",
      "g: 3\n",
      "g: 5\n",
      "g: 8\n",
      "Generator return value: Done\n"
     ]
    }
   ],
   "source": [
    "# 2.使用yield创建生成器\n",
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a, b = b, a + b\n",
    "        n += 1\n",
    "    return 'Done'\n",
    "print('1.生成器返回的是一个对象:',fib(6)) \n",
    "print('2.使用for循环访问yield的输出:', end=' ')\n",
    "for i in fib(6):         # 使用for循环访问数据，但无法得到return后的结果\n",
    "    print(i, end=' ')\n",
    "\n",
    "def fib2(num):\n",
    "    f = fib(num)\n",
    "    while True:\n",
    "        try:\n",
    "            x = next(f)\n",
    "            print('g:', x)\n",
    "        except StopIteration as err:\n",
    "            print('Generator return value:', err.value)\n",
    "            break\n",
    "fib2(6)     # 可以接收返回的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "杨辉三角练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def triangles(rows):\n",
    "#     assert rows>=3 , 'The rows is incorrect.'   # 前两行数据已知，所以杨辉三角行数要大于3,\n",
    "    n = 1\n",
    "#     row_1, row_2 = [1], [1,1]   # 赋初值\n",
    "    row_new = []\n",
    "    while n <= rows:\n",
    "        temp_row = [1]\n",
    "#             for i in range(len(row_2)-1):       # 使用循环的方法比较笨拙\n",
    "#                 temp_row.append(row_2[i] + row_2[i+1])\n",
    "#             temp_row.append(1)      \n",
    "        for i in range(len(row_new)-1):           # range(0,-1)为空，不进行循环\n",
    "            temp_row.append(row_new[i] + row_new[i+1])\n",
    "        temp_row.append(1)\n",
    "        row_new = temp_row  \n",
    "        n += 1\n",
    "        yield row_new"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                 [1]                                  \n",
      "                                [1, 1]                                \n",
      "                              [1, 2, 1]                               \n",
      "                             [1, 3, 3, 1]                             \n",
      "                           [1, 4, 6, 4, 1]                            \n",
      "                         [1, 5, 10, 10, 5, 1]                         \n",
      "                       [1, 6, 15, 20, 15, 6, 1]                       \n",
      "                     [1, 7, 21, 35, 35, 21, 7, 1]                     \n",
      "                   [1, 8, 28, 56, 70, 56, 28, 8, 1]                   \n",
      "                [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]                \n",
      "           [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]            \n",
      "Wall time: 1 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "num = 10\n",
    "f = triangles(num) \n",
    "print('{:^70}'.format(str([1])))\n",
    "for i in range(num):\n",
    "    print('{:^70}'.format(str(next(f))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                 [1]                                  \n",
      "                                [1, 1]                                \n",
      "                              [1, 2, 1]                               \n",
      "                             [1, 3, 3, 1]                             \n",
      "                           [1, 4, 6, 4, 1]                            \n",
      "                         [1, 5, 10, 10, 5, 1]                         \n",
      "                       [1, 6, 15, 20, 15, 6, 1]                       \n",
      "                     [1, 7, 21, 35, 35, 21, 7, 1]                     \n",
      "                   [1, 8, 28, 56, 70, 56, 28, 8, 1]                   \n",
      "                [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]                \n"
     ]
    }
   ],
   "source": [
    "def triangles(rows):\n",
    "    n = 1\n",
    "    L = [1]\n",
    "    while n <= rows:\n",
    "        yield L\n",
    "        L = [L[0]] + [L[i] + L[i+1] for i in range(len(L)-1)] + [L[-1]]\n",
    "f = triangles(7)  \n",
    "num = 10\n",
    "for i in range(num):\n",
    "    print('{:^70}'.format(str(next(f))))        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 迭代器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可迭代对象：\n",
    "\n",
    "1.集合数据类型：list、tuple、dict、set、str \n",
    "\n",
    "2.生成器：()元组生成的、使用yield做返回的函数\n",
    "\n",
    "不可迭代对象：\n",
    "整型、浮点型、布尔型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.list是可迭代对象: True\n",
      "2.字典是可迭代对象: True\n",
      "3.字符串是可迭代对象: True\n",
      "4.生成器是可迭代对象: True\n"
     ]
    }
   ],
   "source": [
    "from collections import Iterable\n",
    "print('1.list是可迭代对象:', isinstance([], Iterable))\n",
    "print('2.字典是可迭代对象:', isinstance({}, Iterable))\n",
    "print('3.字符串是可迭代对象:', isinstance('abc', Iterable))\n",
    "print('4.生成器是可迭代对象:', isinstance((s for s in range(3)), Iterable))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.列表不是迭代器: False\n",
      "2.字典不是迭代器: False\n",
      "3.字符串不是迭代器: False\n",
      "4.生成器是迭代器: True\n",
      "\n",
      "5.使用iter()处理后的列表是迭代器: True\n"
     ]
    }
   ],
   "source": [
    "# 使用next()函数调用的称为迭代器\n",
    "from collections import Iterator\n",
    "print('1.列表不是迭代器:', isinstance([], Iterator))\n",
    "print('2.字典不是迭代器:', isinstance({}, Iterator))\n",
    "print('3.字符串不是迭代器:', isinstance('abc', Iterator))\n",
    "print('4.生成器是迭代器:', isinstance((s for s in range(3)), Iterator))\n",
    "print()  # 列表、字典、字符串使用iter()处理为迭代器\n",
    "print('5.使用iter()处理后的列表是迭代器:', isinstance(iter([]), Iterator))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 5 6 "
     ]
    }
   ],
   "source": [
    "it = iter([1,2,3,4,5,6])  # 将列表变为迭代器来访问数据\n",
    "while True:\n",
    "    try:\n",
    "        x = next(it)\n",
    "        print(x, end = ' ')\n",
    "    except StopIteration:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：集合数据类型如list、dict、str等是Iterable但不是Iterator，不过可以通过iter()函数获得一个Iterator对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 函数式编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 高阶函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.abs函数本身: <built-in function abs>\n",
      "2.使用变量指向函数运算得: 7 | 变量类型: <built-in function abs>\n"
     ]
    }
   ],
   "source": [
    "# 1.变量可以指向函数\n",
    "print('1.abs函数本身:', abs)  \n",
    "f = abs\n",
    "out = f(-7)   # abs() 为函数调用\n",
    "print('2.使用变量指向函数运算得:', out, end=' | ')\n",
    "print('变量类型:', f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<built-in function abs>\n"
     ]
    }
   ],
   "source": [
    "# 2.函数名也是变量\n",
    "abs = 10\n",
    "# print(abs(10))  # 会出现TypeError\n",
    "# 为使函数名在其他模块中生效\n",
    "# import builtins\n",
    "# builtins.abs = 10\n",
    "# print('1.abs值为', abs, 'abs类型:', type(abs))\n",
    "del abs     # 由于这里使用builtins模块，所以del无效，abs仍旧是10\n",
    "print(abs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.函数作为参数传入得: 11\n"
     ]
    }
   ],
   "source": [
    "# 3.高阶函数：函数作为参数传入 ****\n",
    "def add(x, y, f):\n",
    "    return f(x) + f(y)\n",
    "print('1.函数作为参数传入得:', add(-5, 6, abs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(1) map/reduce**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用map()方法创建迭代器: <class 'map'> [1, 4, 9, 16, 25, 36, 49]\n"
     ]
    }
   ],
   "source": [
    "# 1.map(function, Iterable object),将传入的函数一次作用到每个元素，把结果作为迭代器Iterator返回\n",
    "def f(x):\n",
    "    return x * x\n",
    "out = map(f, [1,2,3,4,5,6,7])\n",
    "print('1.使用map()方法创建迭代器:', type(out), list(out))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# next(out) # list(out)已经读取了map迭代器中的内容，所以再使用next会出错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用reduce()逐渐累加: 15\n"
     ]
    }
   ],
   "source": [
    "# 2.reduce(function, iterable object) reduce把结果继续和序列的下一个元素做累积计算\n",
    "from functools import reduce\n",
    "def add(x, y):\n",
    "    return x + y\n",
    "out = reduce(add, [1,2,3,4,5])\n",
    "print('1.使用reduce()逐渐累加:',out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "map、reduce函数练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用map、reduce方法得: 12345 <class 'int'>\n"
     ]
    }
   ],
   "source": [
    "# 1.将字符串转换为整数\n",
    "from functools import reduce\n",
    "DIGITS = {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5}\n",
    "def str2int(s):\n",
    "    fn = lambda x, y : x*10 + y\n",
    "    char2num = lambda s : DIGITS[s]\n",
    "    return reduce(fn, map(char2num, s))\n",
    "out = str2int('12345')\n",
    "print('1.使用map、reduce方法得:', out, type(out))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Adam', 'Lisa', 'Bart']\n"
     ]
    }
   ],
   "source": [
    "# 2.将英文名变为首字母大写，其他小写\n",
    "from functools import reduce\n",
    "def my_captialize(string):     # 该方法bad\n",
    "    str_iter = list(string)\n",
    "    str_iter[0]  = str_iter[0].upper()\n",
    "    str_iter[1:] = [s.lower() for s in str_iter[1:]]  # 使用列表将首字母大写，其余小写\n",
    "#     print(str_iter)    \n",
    "    add_str = lambda x, y: x + y \n",
    "    return reduce(add_str, str_iter)\n",
    "def my_captialize2(string):    # 简略版,good  string.lower()可处理一个字符串，不是单个字符\n",
    "    return string[0].upper() + string[1:].lower()\n",
    "def normalize(name):\n",
    "    return list(map(my_captialize2, name))\n",
    "print(normalize(['adam', 'LISA', 'barT']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "945\n"
     ]
    }
   ],
   "source": [
    "# 3.接受list并求和\n",
    "from functools import reduce\n",
    "def prod(L):\n",
    "    add = lambda x, y : x*y\n",
    "    return reduce(add, L)\n",
    "out = prod([3, 5, 7, 9])\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1231\n",
      "0.33\n",
      "1.字符转换为浮点型结果: 1231.33 <class 'float'>\n"
     ]
    }
   ],
   "source": [
    "# 4.字符串'123.456'转换成浮点数123.456\n",
    "from functools import reduce\n",
    "DIGITS = {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9}\n",
    "def str2float(s):\n",
    "    char2num = lambda x : DIGITS[x]   # 将字符变为整数\n",
    "    integer = lambda x, y : x*10 +y\n",
    "    assert s.count('.') <=1, 'This is a wrong number.'  # 检查小数点数不超过1\n",
    "    str_list = s.split('.')  # 使用 . 分割整数和小数，如果没有小数点，则不分割\n",
    "    if len(str_list) == 1 or str_list[1] == '':   # 没有小数点\n",
    "        integer_num = reduce(integer, map(char2num, str_list[0]))\n",
    "        decimal_num = 0\n",
    "    else:\n",
    "        integer_num = reduce(integer, map(char2num, str_list[0]))\n",
    "        print(integer_num)\n",
    "        decimal_num = reduce(integer, map(char2num, str_list[1])) / 10**(len(str_list[1]))\n",
    "        print(decimal_num)\n",
    "    return integer_num + decimal_num\n",
    "out = str2float('1231.33')\n",
    "print('1.字符转换为浮点型结果:', out, type(out))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(2) filter筛选函数 **"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.经过is_odd函数过滤的结果: [1, 3, 5, 7, 9]\n",
      "2.经过not_empty函数过滤的结果: ['A', 'B', 'C', 'D']\n"
     ]
    }
   ],
   "source": [
    "# 把传入的函数依次作用于序列，然后根据返回值True和False决定保留还是丢弃\n",
    "def is_odd(n):\n",
    "    return n % 2 == 1\n",
    "out = list(filter(is_odd, [1,2,3,4,5,6,7,8,9,10]))\n",
    "print('1.经过is_odd函数过滤的结果:', out)\n",
    "def not_empty(s):\n",
    "    return s and s.strip()\n",
    "out = list(filter(not_empty, ['A', '', 'B', None, 'C', '  ','D', '   ']))\n",
    "print('2.经过not_empty函数过滤的结果:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "filter函数练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100内的素数:\n",
      "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 "
     ]
    }
   ],
   "source": [
    "# 1.打印素数,除2的偶数都不是素数，所以从大于3的奇数中选就行\n",
    "# 这些高阶函数只需转入函数名就行，传入的参数一般只有一个\n",
    "def _odd_iter():   # 产生奇数的惰性序列，无穷的\n",
    "    n = 1\n",
    "    while True:\n",
    "        n += 2\n",
    "        yield n\n",
    "# odd = _odd_iter()\n",
    "# def _not_divisible(n):  # ?????\n",
    "#     return lambda x: x % n > 0\n",
    "def _not_divisible2(n):  # \n",
    "    not_multiple = lambda x: x % n > 0\n",
    "    return not_multiple\n",
    "#     return lambda x: x % n > 0\n",
    "def primes():\n",
    "    yield 2\n",
    "    it = _odd_iter() # 初始序列\n",
    "    while True:\n",
    "        n = next(it) # 返回序列的第一个数\n",
    "        yield n\n",
    "        it = filter(_not_divisible2(n), it) # 构造新序列，注意这里使用函数调用，但返回值是函数\n",
    "        \n",
    "# 打印100以内的素数:\n",
    "print('100内的素数:')\n",
    "for n in primes():\n",
    "    if n < 100:\n",
    "        print(n, end=' ')\n",
    "    else:\n",
    "        break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function _not_divisible at 0x000002C2E2DB1950>\n",
      "<class 'function'>\n",
      "<function _not_divisible.<locals>.<lambda> at 0x000002C2E6A902F0>\n",
      "<class 'function'>\n",
      "False\n",
      "<class 'bool'>\n"
     ]
    }
   ],
   "source": [
    "def _not_divisible(n):\n",
    "    return lambda x: x % n > 0\n",
    "print(_not_divisible)\n",
    "print(type(_not_divisible))\n",
    "\n",
    "print(_not_divisible(3))          # 该函数调用后返回的仍是个函数\n",
    "print(type(_not_divisible(3)))\n",
    "\n",
    "print(_not_divisible(3)(6))       # 这里3传入给n，6传入给x，6 % 3==0所以返False\n",
    "print(type(_not_divisible(3)(6)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1~1000: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999]\n",
      "测试成功!\n"
     ]
    }
   ],
   "source": [
    "# 2.筛选回数，从左到右和从右到左一样的数，例如12321\n",
    "def natural_number():     # 产生自然数的生成器\n",
    "    n = 0\n",
    "    while True:\n",
    "        yield n\n",
    "        n += 1\n",
    "# number = natural_number()\n",
    "def is_palindrome(n):         # 判断是回数\n",
    "    number_str = str(n)  # 先将其转为字符串\n",
    "    reverse_num = int(number_str[::-1])\n",
    "#     return not bool(n - reverse_num)\n",
    "    return n == reverse_num\n",
    "    print(reverse_num)\n",
    "output = filter(is_palindrome, range(1, 1000))\n",
    "print('1~1000:', list(output))\n",
    "if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:\n",
    "    print('测试成功!')\n",
    "else:\n",
    "    print('测试失败!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**(3) sorted函数**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.排序结果: [-36, -5, 3, 4, 7, 77, 99]\n",
      "2.逆排序结果: [99, 77, 7, 4, 3, -5, -36]\n",
      "3.加入key按绝对值后排序结果: [3, 4, -5, 7, -36, 77, 99]\n",
      "4.对字符串的排序: ['Credit', 'Zoo', 'about', 'bob']\n",
      "5.忽略大小写后的排序结果: ['about', 'bob', 'Credit', 'Zoo']\n"
     ]
    }
   ],
   "source": [
    "# sorted()函数也是一个高阶函数，它还可以接收一个key函数来实现自定义的排序\n",
    "# key后面接处理待排序序列的函数名字\n",
    "out = sorted([3,4,-5,7,77,-36,99])\n",
    "print('1.排序结果:', out)\n",
    "out = sorted([3,4,-5,7,77,-36,99], reverse=True)\n",
    "print('2.逆排序结果:', out)\n",
    "out = sorted([3,4,-5,7,77,-36,99], key=abs)\n",
    "print('3.加入key按绝对值后排序结果:', out)\n",
    "out = sorted(['bob', 'about', 'Zoo', 'Credit'])\n",
    "print('4.对字符串的排序:', out)\n",
    "out = sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)\n",
    "print('5.忽略大小写后的排序结果:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "sorted函数练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.按姓名排序: [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]\n",
      "2.按成绩排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]\n",
      "3.分数和姓名互换后结果: [(66, 'Bart'), (75, 'Bob'), (88, 'Lisa'), (92, 'Adam')]\n"
     ]
    }
   ],
   "source": [
    "# 按学生姓名排序\n",
    "L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]\n",
    "def by_name(t):   # 取出姓名返回\n",
    "    return  t[0].lower()\n",
    "out = sorted(L, key=by_name)\n",
    "print('1.按姓名排序:', out)\n",
    "\n",
    "def by_score(t):\n",
    "    return t[1]\n",
    "out = sorted(L, key=by_score, reverse=True)\n",
    "print('2.按成绩排序:', out)\n",
    "L_new = [(j, i) for i,j in L]    # 不使用key的还默认使用各元素前面的内容排序\n",
    "print('3.分数和姓名互换后结果:', sorted(L_new))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 返回函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.返回函数: <function lazy_sum.<locals>.my_sum at 0x000002C2E6A4B7B8> <class 'function'>\n",
      "2.调用返回的函数: 20 <class 'int'>\n",
      "3.每次调用返回不同函数: True\n",
      "  调用返回的函数: 15 15\n"
     ]
    }
   ],
   "source": [
    "# 函数返回值仍是一个函数，相关参数和变量都保存在返回的函数中，该结构称为闭包\n",
    "def lazy_sum(*args):\n",
    "    def my_sum():\n",
    "        summary_out = 0\n",
    "        for n in args:\n",
    "            summary_out += n\n",
    "        return summary_out    # 返回求和的结果\n",
    "    return my_sum             # 返回求和的函数\n",
    "func = lazy_sum(*range(2,7))    # 可变参数，对列表、元组和字典前面切记加 *\n",
    "print('1.返回函数:',func, type(func))\n",
    "out = func()\n",
    "print('2.调用返回的函数:', out, type(out))\n",
    "func1 = lazy_sum(1,2,3,4,5)\n",
    "func2 = lazy_sum(1,2,3,4,5)\n",
    "print('3.每次调用返回不同函数:', func1 != func2)\n",
    "print('  调用返回的函数:', func1(), func2())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 * 4 * 9 * \n",
      "1.返回3个函数: \n",
      " <function count.<locals>.f at 0x000002C2E6A4B9D8> <class 'function'> \n",
      " <function count.<locals>.f at 0x000002C2E6A4BD90> <class 'function'> \n",
      " <function count.<locals>.f at 0x000002C2E6A41B70> <class 'function'>\n",
      "2.调用3个函数: 9 9 9\n"
     ]
    }
   ],
   "source": [
    "# 闭包在调用返回函数时对内部循环变量的处理\n",
    "def count():\n",
    "    fs = []\n",
    "    for i in range(1,4):\n",
    "        def f():\n",
    "            return i * i\n",
    "        fs.append(f)      # 这里是将函数加到列表中\n",
    "        print(fs[i-1](), end= ' * ')  # 函数立刻执行\n",
    "    return fs\n",
    "f1,f2,f3 = count()\n",
    "print()\n",
    "print('1.返回3个函数:','\\n', f1, type(f1), '\\n',f2, type(f2),'\\n',f3, type(f3))\n",
    "print('2.调用3个函数:', f1(), f2(), f3())   # 3个函数都返回时才调用变量i，而该变量已经变为3了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 * 4 * 9 * \n",
      "1.返回3个函数: \n",
      " <function count2.<locals>.f.<locals>.g at 0x000002C2E6A22EA0> <class 'function'> \n",
      " <function count2.<locals>.f.<locals>.g at 0x000002C2E6A90A60> <class 'function'> \n",
      " <function count2.<locals>.f.<locals>.g at 0x000002C2E6A90598> <class 'function'>\n",
      "2.调用3个函数: 1 4 9\n"
     ]
    }
   ],
   "source": [
    "# 闭包调用返回函数时使用内部循环变量\n",
    "def count2():\n",
    "    fs = []\n",
    "    def f(temp):\n",
    "        def g():\n",
    "            return temp * temp\n",
    "        return g     # 返回函数g\n",
    "    for i in range(1,4):\n",
    "        fs.append(f(i))  # f(i)立刻被执行，因此i的当前值被传入f()\n",
    "        print(fs[i-1](), end=' * ')  # 函数立刻执行\n",
    "    return fs\n",
    "f1, f2, f3 = count2()\n",
    "print()\n",
    "print('1.返回3个函数:','\\n', f1, type(f1), '\\n',f2, type(f2),'\\n',f3, type(f3))\n",
    "print('2.调用3个函数:', f1(), f2(), f3())  #"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：\n",
    "\n",
    "1.返回一个函数时，牢记该函数并未执行，返回函数中不要引用任何可能会变化的变量。\n",
    "\n",
    "2.这个闭包函数结构有些像类方法：\n",
    "首先定义类实例，f1, f2, f3 = count2()，然后调用类的方法f1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "返回函数练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 5\n",
      "测试通过!\n"
     ]
    }
   ],
   "source": [
    "# 方法一、利用闭包返回一个计数器函数，每次调用它返回递增整数，只用生成器更简单\n",
    "def createCounter():\n",
    "    def g():\n",
    "        n = 1\n",
    "        while True:\n",
    "            yield n\n",
    "            n += 1\n",
    "        return g\n",
    "    t = g()\n",
    "    def counter():       \n",
    "        return next(t)\n",
    "    return counter\n",
    "counterA = createCounter()\n",
    "print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5\n",
    "counterB = createCounter()\n",
    "if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:\n",
    "    print('测试通过!')\n",
    "else:\n",
    "    print('测试失败!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0]\n",
      "[0, 0]\n",
      "[0, 0, 0]\n",
      "[0, 0, 0, 0]\n",
      "[0, 0, 0, 0, 0]\n",
      "1 2 3 4 5\n"
     ]
    }
   ],
   "source": [
    "# 方法二、使用列表\n",
    "def createCounter2():\n",
    "    m = []\n",
    "    def counter():\n",
    "        m.append(0)\n",
    "        print(m)  \n",
    "        return len(m)\n",
    "    return counter\n",
    "counterA = createCounter2()\n",
    "print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 5\n"
     ]
    }
   ],
   "source": [
    "# 方法三、使用nonlocal用于内部函数更改外层函数的外部变量的关键字\n",
    "def createCounter3():\n",
    "    a = 0\n",
    "    def counter():\n",
    "        nonlocal a   # 声明a不是counter的局部变量\n",
    "        a = a+1\n",
    "        return a\n",
    "    return counter\n",
    "counterA = createCounter3()\n",
    "print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 匿名函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用匿名函数的结果: <map object at 0x000002C2E6A6A048> [1, 4, 9, 16, 25, 36, 49]\n",
      "2.调用匿名函数赋值变量的结果: <map object at 0x000002C2E6A6AA58> [1, 4, 9, 16, 25, 36, 49]\n",
      "3.返回匿名函数并调用: <function build.<locals>.<lambda> at 0x000002C2E6A418C8> 5\n"
     ]
    }
   ],
   "source": [
    "# 关键字lambda表示匿名函数，冒号前面的是函数参数，后面是表达式\n",
    "# function_name = lambda parameters:expression\n",
    "# 1.直接使用匿名函数，不定义函数名\n",
    "map_out = map(lambda x : x * x, [1,2,3,4,5,6,7])  # 使用map函数调用函数名square_func\n",
    "print('1.调用匿名函数的结果:', map_out, list(map_out))\n",
    "\n",
    "# 2.匿名函数可以没有函数名直接调用，没有函数名称冲突；也可以将其赋值给一个变量\n",
    "square_func = lambda x : x * x\n",
    "map_out = map(square_func, [1,2,3,4,5,6,7])\n",
    "print('2.调用匿名函数赋值变量的结果:', map_out, list(map_out))\n",
    "\n",
    "# 3.匿名函数作为返回值返回\n",
    "def build(x, y):\n",
    "    return lambda : x * x + y * y  # 表达式中没有的变量是外部变量，冒号前没有\n",
    "out = build(1,2)\n",
    "print('3.返回匿名函数并调用:', out, out())   # out是返回的函数，out()是调用该函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "匿名函数练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用匿名函数得到奇数: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]\n",
      "2.调用匿名函数得到奇数: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]\n"
     ]
    }
   ],
   "source": [
    "# \"\"\"将下面函数改为匿名函数\n",
    "# def is_odd(n):\n",
    "#    return n % 2 == 1\n",
    "#\"\"\"\n",
    "# 方法一\n",
    "is_odd = lambda n : n % 2 ==1  # 匿名函数定义\n",
    "out = list(filter(is_odd, range(1,20)))\n",
    "print('1.调用匿名函数得到奇数:', out)\n",
    "# 方法二\n",
    "f = lambda n : [x for x in n if x % 2 == 1]\n",
    "print('2.调用匿名函数得到奇数:', f(range(1,20)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4 装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.函数赋值给变量的结果: <function hello at 0x000002C2E6A41378> <class 'function'>\n",
      "2.用赋值后的变量调用函数: hello world!\n",
      "3.函数的名称: hello hello\n"
     ]
    }
   ],
   "source": [
    "# 函数是对象，可以赋值给变量，调用该变量就是调用函数\n",
    "def hello():\n",
    "    print('hello world!')\n",
    "func = hello # 将hello函数赋值给变量func，则该变量已经指向函数\n",
    "print('1.函数赋值给变量的结果:', func, type(func))\n",
    "print('2.用赋值后的变量调用函数:', end=' ')\n",
    "func()\n",
    "print('3.函数的名称:', hello.__name__, func.__name__)  #函数赋值给变量后，所以函数名称一致\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.修饰后的函数输出:\n",
      "call now():\n",
      "2018-08-01\n",
      "2.修饰后的函数名称: wrapper\n"
     ]
    }
   ],
   "source": [
    "# 在代码运行期间动态增加功能的方式，称为装饰器Decorator\n",
    "#(参数和返回值都是函数，只是返回的函数是参数的函数经过修饰增加某些功能得到的)\n",
    "# 1.这里定义一个打印日志的装饰器\n",
    "def log(func):                                # 这里log函数，参数是函数，返回值也是函数\n",
    "    def wrapper(*args, **kw):\n",
    "        print('call %s():' % func.__name__)   # 增加的输出\n",
    "        return func(*args, **kw)              # 返回func()函数调用的结果\n",
    "    return wrapper                            # 返回wrapper函数\n",
    "\n",
    "@log           # 将now函数放到log修饰器中\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "print('1.修饰后的函数输出:')\n",
    "now()    # 调用修饰后的函数\n",
    "print('2.修饰后的函数名称:', now.__name__)      # 经过修饰的函数名称已经改变了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.修饰后的函数输出:\n",
      "call now():\n",
      "2018-08-01\n"
     ]
    }
   ],
   "source": [
    "# 2.装饰器的直观解释\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "def log(func):                                # 这里log函数，参数是函数，返回值也是函数\n",
    "    def wrapper(*args, **kw):\n",
    "        print('call %s():' % func.__name__)   # 增加的输出\n",
    "        return func(*args, **kw)              # 返回func()函数调用的结果\n",
    "    return wrapper                            # 返回wrapper函数\n",
    "print('1.修饰后的函数输出:')\n",
    "now = log(now)              # 这里与@log实现的功能是一样的，都是将now函数转入log返回wrapper函数\n",
    "now()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.修饰后的带参数的函数输出:\n",
      "execute now():\n",
      "2018-08-01\n",
      "2.@后语句的直观解释:\n",
      "execute now():\n",
      "2018-08-01\n"
     ]
    }
   ],
   "source": [
    "# 3.传入参数的装饰器，需要编写返回装饰器的高阶函数\n",
    "def log(text):\n",
    "    def decorator(func):            # 传入需经过修饰的函数名称\n",
    "        def wrapper(*args, **kw):   # func函数的可变参数定义\n",
    "            print('%s %s():' %(text, func.__name__))\n",
    "            return func(*args, **kw)         # 返回func函数的调用结果\n",
    "        return wrapper                       # 返回func函数经过修饰得到的wrapper函数\n",
    "    return decorator                         # 返回装饰器函数\n",
    "\n",
    "@log('execute')\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "print('1.修饰后的带参数的函数输出:')\n",
    "now()\n",
    "\n",
    "print('2.@后语句的直观解释:')\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "now = log('execute')(now)  # @log('execute')实际执行的方式\n",
    "now()\n",
    "# 以上装饰器存在问题，now函数名称变为wrapper，可能会影响某些依赖函数签名的代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**完整的装饰器定义方式**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用装饰器的结果:\n",
      "excute now()\n",
      "2018-08-01\n",
      "2.修饰后函数的名称: now\n"
     ]
    }
   ],
   "source": [
    "# 将修饰器中函数的名称变为原函数名\n",
    "import functools\n",
    "def log(text):\n",
    "    def decorator(func):\n",
    "        @functools.wraps(func)       # 相当于wrapper.__name__ = func.__name__\n",
    "        def wrapper(*args, **kw):\n",
    "            print('%s %s()' % (text, func.__name__))\n",
    "            return func(*args, **kw)\n",
    "        return wrapper\n",
    "    return decorator\n",
    "@log('excute')\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "print('1.使用装饰器的结果:')\n",
    "now()\n",
    "print('2.修饰后函数的名称:', now.__name__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "装饰器练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fast executed in 0.002101241015845301 ms\n",
      "slow executed in 0.12415300331231727 ms\n",
      "测试成功！\n"
     ]
    }
   ],
   "source": [
    "# 1.设计打印函数执行时间的装饰器\n",
    "import functools\n",
    "import time\n",
    "def metric(func):\n",
    "    @functools.wraps(func)  # 将装饰后的函数名保持不变\n",
    "    def wrapper(*args, **kw):\n",
    "        begin_time = time.clock()\n",
    "        out = func(*args, **kw)\n",
    "        execute_time = time.clock() - begin_time\n",
    "        print('%s executed in %s ms' % (func.__name__, execute_time))\n",
    "        return out\n",
    "    return wrapper\n",
    "\n",
    "@metric\n",
    "def fast(x, y):\n",
    "    time.sleep(0.0012)\n",
    "    return x + y;\n",
    "@metric\n",
    "def slow(x, y, z):\n",
    "    time.sleep(0.1234)\n",
    "    return x * y * z;\n",
    "\n",
    "f = fast(11, 22)\n",
    "s = slow(11, 22, 33)\n",
    "if f == 33 and s == 7986:\n",
    "    print('测试成功！')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用无参数的形式:\n",
      "() now():\n",
      "2018-08-01\n",
      "2.使用有参数的形式:\n",
      "('excute',) now():\n",
      "2018-08-01\n"
     ]
    }
   ],
   "source": [
    "# 2.修饰器即支持@log，也支持@log('execute')   !!!!!!!!!!!!!!!!!存在问题\n",
    "import functools\n",
    "def log(*decorator_args):                 # 使修饰器具有可变参数\n",
    "    def decorator(func):        # 传入待装饰的函数\n",
    "        @functools.wraps(func)  # 保持函数名不变\n",
    "        def wrapper(*args, **kw):\n",
    "            print('%s %s():' % (decorator_args, func.__name__))\n",
    "            return func(*args, **kw)  # 返回调用func函数的结果\n",
    "        return wrapper                # 返回装饰后的函数\n",
    "    return decorator                  # 返回带参数的装饰器\n",
    "\n",
    "@log()           # 将now函数放到log修饰器中\n",
    "def now():\n",
    "    print('2018-08-01')\n",
    "print('1.使用无参数的形式:')\n",
    "now()\n",
    "\n",
    "@log('excute')\n",
    "def now():\n",
    "    print('2018-08-01')    \n",
    "print('2.使用有参数的形式:')   \n",
    "now()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.5 偏函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.二进制转换的结果: 64\n"
     ]
    }
   ],
   "source": [
    "# 改变函数默认参数的值\n",
    "# 1.不使用偏函数来定义新函数来改变默认的参数值\n",
    "def int2(x, base=2):       # 使int函数默认值base为2\n",
    "    return int(x, base)\n",
    "# 2.使用偏函数的方法定义新函数改变函数默认参数\n",
    "import functools\n",
    "int2 = functools.partial(int, base=2)\n",
    "out = int2('1000000')\n",
    "print('1.二进制转换的结果:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "参数个数很多，使用偏函数可以创建一个新函数，将原来函数的部分参数修改为自定义的默认值"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
